﻿using System;
using System.Collections.Generic;
using System.Text;
using HtmlAgilityPack;
using TotalCommander.Plugin.Wfx;
using NI.Vfs;
using System.IO;
using System.Xml.XPath;


namespace TotalCommander.Plugin.WywrotaExplorer.Wfx
{
    public class HtmlDataManager
    {
        private HtmlDocument hd = new HtmlDocument();

        private int currLevel;

        private HtmlWeb hw = new HtmlWeb();

        private HtmlNode[][] menus;

        public HtmlNode[][] Menus
        {
            get { return menus; }
            set { menus = value; }
        }

        private string domainName, repeatPath;

        public string DomainName
        {
            get { return domainName; }
            set { domainName = value; }
        }

        private string[] sciezkiKatalogow = new string[4];

        private string[] sciezkiPlikow = new string[4];

        public string[] Sciezki
        {
            get { return sciezkiKatalogow; }
            set { sciezkiKatalogow = value; }
        }

        public HtmlDataManager(string fileName)
        {
            domainName = "http://teksty.wywrota.pl";
            repeatPath = "//a[@class='arRight']";
            sciezkiKatalogow = new string[]{
                "",
                "//ul[@class='mainNav']/li[@class=' n1' or @class=' n2']/a",
                "//a[@class='letter']",
                "//div[@class='byLetterAll']/a",
                //"//a[substring(@class,1,5)='title']",
                "//a[starts-with(@class,'title')]",
                "//div[starts-with(@class,'songContent')]"

            }; 
            XPathExpression xpe = XPathExpression.Compile(sciezkiKatalogow[2]);
            sciezkiPlikow = new string[]{
                "",
                "",
                "",
                "",
                "",
                ""
            };

  
            //HtmlNode[] nody = new HtmlNode[]{
            //    HtmlNode.CreateNode("<a href=\""+domainName+"/pl.html\">polskie</a>"),
            //    HtmlNode.CreateNode("<a href=\""+domainName+"/zag.html\">zagraniczne</a>")
            //};
            currLevel = 0;
            menus = new HtmlNode[sciezkiKatalogow.Length][];
            menus[0] = new HtmlNode[] { HtmlNode.CreateNode("<a href=\"" + domainName + "\">wywrota</a>") };
        }
        
        public HtmlNode GetNode(string path)
        {
            //currLevel = SplitPath(path).Length;
            string[] tokens = SplitPath(path);
            currLevel = tokens.Length;
            string href = "";
            HtmlNode node = GetNode(menus[currLevel - 1], tokens[tokens.GetUpperBound(0)]);
            if (node != null)
            {
                href = node.Attributes["href"].Value;
                if (!href.Contains(DomainName)) href = DomainName + "/" + href;
                Menus[currLevel] = GetRemoteNodes(href, Sciezki[currLevel]);
            }
            else
            {
                for (int i = 0; i <= currLevel; i++)
                {
                    href = GetNode(Menus[i], tokens[i]).Attributes["href"].Value;
                    if (!href.Contains(DomainName)) href = DomainName + "/" + href;
                    Menus[i + 1] = GetRemoteNodes(href, Sciezki[i + 1]);
                }
            }
            if (currLevel == Menus.GetUpperBound(0))
            {
                Menus[currLevel] = GetRepeatedContent(currLevel, href);
            }
            //if(node =  node = GetNode(menus[currLevel - 1], tokens[tokens.GetUpperBound(0)]);
            return node ?? GetNode(Menus[currLevel - 1], tokens[tokens.GetUpperBound(0)]);

        }

        public HtmlNode GetNode(HtmlNode[] coll, string nodeInnerText)
        {
            if (coll != null)
            {
                foreach (HtmlNode node in coll)
                {
                    if (node.InnerHtml == nodeInnerText)
                        return node;
                }
            }
            return null;
        }

        public string GetRemoteData(string url, string path)
        {
            HtmlDocument hd1 = new HtmlWeb().Load(url);
            return hd1.DocumentNode.SelectSingleNode(path).InnerHtml;
        }

        public string GetRemoteData(string path)
        {
            string href = GetNode(path).Attributes["href"].Value;
            if (!href.Contains(domainName)) href = domainName + href;
            return GetRemoteData(href, sciezkiKatalogow[currLevel]);

        }

        public HtmlNode[] GetRemoteNodes(string href, string sciezkiLevel)
        {
            hd = hw.Load(href);
            HtmlNode[] result;
            HtmlNodeCollection coll = hd.DocumentNode.SelectNodes(sciezkiLevel);
            result = new HtmlNode[coll.Count];
            coll.CopyTo(result, 0);
            return result;
        }

        public HtmlNode[] GetRepeatedContent(int level, string href)
        {
            HtmlNode[] temp1 = menus[level];
            while (hd.DocumentNode.SelectSingleNode(repeatPath) != null)
            {
                href = hd.DocumentNode.SelectSingleNode(repeatPath).Attributes["href"].Value;
                if (!href.Contains(domainName)) href = domainName + "/" + href;
                HtmlNode[] temp2 = GetRemoteNodes(href, sciezkiKatalogow[level]);
                HtmlNode[] temp3 = new HtmlNode[temp1.Length + temp2.Length];
                temp1.CopyTo(temp3, 0); temp2.CopyTo(temp3, temp1.Length);
                temp1 = temp3;
            }
            return temp1;
        }

        public static string[] SplitPath(string path)
        {
            string[] tokeny = path.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries);
            return tokeny;
        }

        public FindData ToFindData(HtmlNode node)
        {
            var findData = new FindData();
            switch (node.NodeType)
            {
                case HtmlNodeType.Element:
                    if (node.Name.ToLower() == "a")
                    {
                        findData.FileName = node.InnerHtml;
                        //if(currLevel==3)
                        HtmlNode hn = node.SelectSingleNode("..");
                        try
                        {
                            findData.FileSize = currLevel == 3 ? Int32.Parse(node.NextSibling.InnerText
                                            .Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries)[0]) : 1;
                        }
                        catch (Exception)
                        {
                            findData.FileSize = 1;
                        }
                        if (node.SelectNodes("../a[starts-with(@class,'title')]")!=null)
                            findData.Attributes = FileAttributes.ReadOnly;
                        else
                            findData.Attributes |= FileAttributes.Directory;
                    }
                    else
                    {
                        string[] tokens = node.XPath.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
                        string nodeName = tokens[tokens.GetUpperBound(0)];
                        findData.FileSize = node.ChildNodes.Count;
                    }
                    //findData.Attributes |= FileAttributes.ReadOnly;
                    //if (currLevel == menus.GetUpperBound(0) - 1) findData.Attributes = FileAttributes.ReadOnly;
                    break;

                case HtmlNodeType.Text:
                    findData.FileSize = node.InnerHtml.Length;
                    findData.LastWriteTime = DateTime.Now;
                    break;

                case HtmlNodeType.Comment:
                    findData.FileName = "#comment";
                    findData.FileSize = node.InnerHtml.Length;
                    findData.Attributes = FileAttributes.ReadOnly;
                    break;
            }
            return findData;
        }
        public class LevelDataStructure
        {
            private HtmlWeb hw;
            XPathExpression menusXPath,
                isFilePath,
                repeatPath;
            private string dataUrl;
                //menusXPath,
                //isFilePath,
                //repeatPath;
            HtmlNode[] menuItems;

            public HtmlNode this[int index]
            {
                get { return menuItems[index]; }
                set { menuItems[index] = value; }
            }


            public LevelDataStructure(string url, string _menusXPath, string _repeatPath = null, string _isFilePath = null)
            {
                hw = new HtmlWeb();
                dataUrl = url;
                //menusXPath = _menusXPath;
                //repeatPath = _repeatPath;
                //isFilePath = _isFilePath;
            }

            public LevelDataStructure(string _menusXPath, string _repeatPath = null, string _isFilePath = null)
            {
                hw = new HtmlWeb();
                menusXPath = XPathExpression.Compile(_menusXPath);
                repeatPath = XPathExpression.Compile(_repeatPath);
                isFilePath = XPathExpression.Compile(_isFilePath);
                //menusXPath = _menusXPath;
                //repeatPath = _repeatPath;
                //isFilePath = _isFilePath;
            }

            public void Initialize(string url)
            {
                dataUrl = url;
                HtmlNodeCollection hnc = hw.Load(dataUrl).DocumentNode.SelectNodes(menusXPath.Expression);
                menuItems = new HtmlNode[hnc.Count];
                hnc.CopyTo(menuItems, 0);
            }

            /// <summary>
            /// zwraca node z danym tekstem
            /// </summary>
            public HtmlNode GetNode(string tekst)
            {
                foreach (HtmlNode node in menuItems)
                {
                    if (node.InnerText == tekst) return node;
                }
                return null;
            }

            public string DataUrl
            {
                get { return dataUrl; }
                set { dataUrl = value; }
            }
            
        }

        /// <summary>
        /// struktura zawierająca dane poziomow
        /// </summary>
        private LevelDataStructure[] levele;

    }
}
